//
// Created by tang on 22-9-12.
//

#ifndef EXAMPLEVK_VULKAN_APP_BASE_HPP
#define EXAMPLEVK_VULKAN_APP_BASE_HPP

#include <vulkan/vulkan.hpp>

namespace core::input {
    class InputContext;
}

namespace core::app {
    class Window;

    class VulkanApp {
    public:
        virtual ~VulkanApp();
        void AttachWindow(Window *window);
        void DetachWindow();
        void Update();

    protected:
        VulkanApp();
        virtual void OnInitialize()    = 0;
        virtual void OnCleanup()       = 0;
        virtual void OnWindowResized() = 0;
        virtual vk::DescriptorSetLayout OnCreateDescriptorSetLayout() = 0;
        virtual vk::Pipeline
                     OnCreateGraphicsPipeline(vk::Extent2D screen_size) = 0;
        virtual void OnRecordRenderCommand(vk::CommandBuffer buffer,
                                           u32               currentFrame,
                                           u32               imageIdx,
                                           vk::Extent2D      screen_size)    = 0;
        virtual void OnUpdateBuffers(u32 imageIdx, u32 currentFrame) = 0;

        void ReCreateSwapchianAndStuff();
        std::tuple<vk::UniqueBuffer, vk::UniqueDeviceMemory>
             CreateBuffer(vk::DeviceSize bufferSize, vk::BufferUsageFlags usage,
                          vk::MemoryPropertyFlags property);
        void UploadBufferData(vk::DeviceMemory memory, const void *data,
                              vk::DeviceSize length, vk::DeviceSize offset = 0);
        void CopyBuffer(vk::Buffer src, vk::Buffer dst, vk::DeviceSize size);
        static u32 MaxInFlightImageCount();

    private:
        void VulkanInit();

    protected:
        GLFWwindow                      *m_window;
        const input::InputContext       *m_input;
        vk::UniqueInstance               m_instance;
        vk::UniqueDebugUtilsMessengerEXT m_debug_utils;
        vk::UniqueSurfaceKHR             m_surface;
        vk::PhysicalDevice               m_physical_device;
        i32                              m_graphic_family;
        i32                              m_present_family;
        vk::UniqueDevice                 m_device;
        vk::Queue                        m_graphics_queue;
        vk::Queue                        m_present_queue;
        vk::SurfaceFormatKHR             m_surface_format;
        vk::PresentModeKHR               m_present_mode;
        u32                              m_image_count;
        vk::SurfaceTransformFlagBitsKHR  m_transform;
        vk::UniqueDescriptorSetLayout    m_descriptor_set_layout;
        vk::UniquePipelineLayout         m_pipeline_layout;
        vk::UniqueDescriptorPool         m_descriptor_pool;
        vk::UniqueCommandPool            m_command_pool;
        std::vector<vk::UniqueSemaphore> m_image_available_semaphores;
        std::vector<vk::UniqueSemaphore> m_render_finished_semaphores;
        std::vector<vk::UniqueFence>     m_in_flight_fences;
        u32                              m_current_frame;

        // swap chain and relative things
        vk::UniqueSwapchainKHR               m_swapchain;
        std::vector<vk::Image>               m_images;
        std::vector<vk::UniqueImageView>     m_views;
        vk::UniqueRenderPass                 m_render_pass;
        vk::UniquePipeline                   m_render_pipeline;
        std::vector<vk::UniqueFramebuffer>   m_frame_buffers;
        std::vector<vk::UniqueCommandBuffer> m_command_buffers;
        vk::Extent2D                         m_swapchian_size;
    };
}  // namespace core::app

#endif  // EXAMPLEVK_VULKAN_APP_BASE_HPP
